package com.autumn.mybatis.criterion;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import com.autumn.mybatis.criterion.clauses.AbstractCriteriaClause;
import com.autumn.mybatis.criterion.expressions.ColumnExpression;
import com.autumn.mybatis.criterion.expressions.AbstractCriteriaExpression;
import com.autumn.mybatis.criterion.expressions.AbstractExpression;
import com.autumn.mybatis.criterion.impl.CriteriaSectionImpl;
import com.autumn.mybatis.metadata.EntityTable;
import com.autumn.util.ExceptionUtils;

/**
 * 抽象条件
 * 
 * @author 老码农
 *
 *         2017-10-26 10:28:16
 */
abstract class AbstractCriteria<TCriteria extends AbstractCriteria<?>> extends AbstractParser implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 5855761380563961503L;
	private AbstractCriteriaExpression fullEexpression = null;
	protected LockModeEnum lockMode;

	/**
	 * 无表实例化
	 */
	public AbstractCriteria() {
		super();
		this.lockMode = LockModeEnum.NONE;
	}

	/**
	 * 转入 entityClass 实例化
	 * 
	 * @param entityClass
	 *            实体类型
	 */
	public AbstractCriteria(Class<?> entityClass) {
		super(entityClass);
		this.lockMode = LockModeEnum.NONE;
	}

	/**
	 * 重置
	 */	
	public void reset() {
		this.lockMode = LockModeEnum.NONE;
		this.fullEexpression = null;
	}

	/**
	 * 转入 EntityTable 实例化
	 * 
	 * @param entityTable
	 *            实体表
	 */
	public AbstractCriteria(EntityTable entityTable) {
		super(entityTable);
		this.lockMode = LockModeEnum.NONE;
	}

	/**
	 * 创建条件集合
	 * 
	 * @return
	 */
	protected final List<AbstractCriteriaClause> createCriteriaSections() {
		if (this.fullEexpression != null) {
			return fullEexpression.createSections();
		}
		return new ArrayList<>();
	}

	/**
	 * 返回自身 this
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected TCriteria returnThis() {
		return (TCriteria) this;
	}

	/**
	 * 锁
	 * 
	 * @param mode
	 *            模式
	 * @return
	 */
	public TCriteria lock(LockModeEnum mode) {
		this.lockMode = mode;
		return this.returnThis();
	}
	
	/**
	 * 更新锁
	 * 	
	 * @return
	 */
	public TCriteria lockByUpdate() {		
		return this.lock(LockModeEnum.UPDATE);
	}
	
	/**
	 * 共享锁
	 * 	
	 * @return
	 */
	public TCriteria lockByShare() {		
		return this.lock(LockModeEnum.SHARE);
	}
	
	/**
	 * 无锁
	 * 	
	 * @return
	 */
	public TCriteria lockByNone() {		
		return this.lock(LockModeEnum.NONE);
	}

	/**
	 * 条件
	 * 
	 * @param operater
	 *            逻辑运算符
	 * @param expression
	 *            条件表达式
	 * @return
	 */
	public TCriteria criteria(LogicalOperatorEnum operater, AbstractCriteriaExpression expression) {
		ExceptionUtils.checkNotNull(operater, "operater");
		ExceptionUtils.checkNotNull(expression, "expression");
		if (this.fullEexpression == null) {
			this.fullEexpression = expression;
		} else {
			this.fullEexpression = AbstractExpression.logical(operater, this.fullEexpression, expression);
		}
		return this.returnThis();
	}

	/**
	 * 使用 And 创建条件
	 * 
	 * @param expression
	 *            条件表达式
	 * @return
	 */
	public TCriteria criteria(AbstractCriteriaExpression expression) {
		return this.criteria(LogicalOperatorEnum.AND, expression);
	}

	/**
	 * 条件
	 * 
	 * @param operater
	 *            逻辑运算符
	 * @param criteriaBeans
	 *            条件 Beans 集合
	 * @return
	 */
	public <E extends ICriteriaBean> TCriteria criteria(LogicalOperatorEnum operater, List<E> criteriaBeans) {
		ExceptionUtils.checkNotNull(operater, "operater");
		AbstractCriteriaExpression exp = AbstractExpression.parserBean(this.getEntityTable(), criteriaBeans);
		if (exp != null) {
			return this.criteria(operater, exp);
		}
		return this.returnThis();
	}

	/**
	 * 使用 And 创建条件
	 * 
	 * @param criteriaBeans
	 *            条件 Beans 集合
	 * @return
	 */
	public <E extends ICriteriaBean> TCriteria criteria(List<E> criteriaBeans) {
		return this.criteria(LogicalOperatorEnum.AND, criteriaBeans);
	}

	/**
	 * 条件
	 * 
	 * @param operater
	 *            逻辑运算符
	 * @param criteriaOperator
	 *            条件运算符
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria criteria(LogicalOperatorEnum operater, CriteriaOperatorEnum criteriaOperator, String name,
			Object value) {
		ExceptionUtils.checkNotNull(criteriaOperator, "criteriaOperator");
		ColumnExpression column = column(name);
		AbstractCriteriaExpression exp = AbstractExpression.binary(criteriaOperator, column, constant(column, value));
		return criteria(operater, exp);
	}

	/**
	 * And 条件表达式
	 * 
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria criteria(CriteriaOperatorEnum criteriaOperator, String name, Object value) {
		return this.criteria(LogicalOperatorEnum.AND, criteriaOperator, name, value);
	}

	/**
	 * 清除条件
	 * 
	 * @return
	 */
	public TCriteria clearCriteria() {
		this.fullEexpression = null;
		return this.returnThis();
	}

	/**
	 * eq(=)条件
	 * 
	 * @param operater
	 *            逻辑运算符
	 * @param name
	 *            名称
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria eq(LogicalOperatorEnum operater, String name, Object value) {
		return this.criteria(operater, CriteriaOperatorEnum.EQ, name, value);
	}

	/**
	 * And eq(=)条件
	 * 
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria eq(String name, Object value) {
		return eq(LogicalOperatorEnum.AND, name, value);
	}

	/**
	 * notEq(<>)不等于条件
	 * 
	 * @param operater
	 *            逻辑运算符
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria notEq(LogicalOperatorEnum operater, String name, Object value) {
		return criteria(operater, CriteriaOperatorEnum.NOT_EQ, name, value);
	}

	/**
	 * And notEq(<>)不等于条件
	 * 
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria notEq(String name, Object value) {
		return notEq(LogicalOperatorEnum.AND, name, value);
	}

	/**
	 * 大于(>)
	 * 
	 * @param operater
	 *            逻辑运算符
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria gt(LogicalOperatorEnum operater, String name, Object value) {
		return criteria(operater, CriteriaOperatorEnum.GT, name, value);
	}

	/**
	 * And 大于(>)
	 * 
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria gt(String name, Object value) {
		return gt(LogicalOperatorEnum.AND, name, value);
	}

	/**
	 * 大于或等于(>=)
	 * 
	 * @param operater
	 *            逻辑运算符
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria ge(LogicalOperatorEnum operater, String name, Object value) {
		return criteria(operater, CriteriaOperatorEnum.GE, name, value);
	}

	/**
	 * And 大于或等于(>=)
	 * 
	 * @param operater
	 *            逻辑运算符
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria ge(String name, Object value) {
		return ge(LogicalOperatorEnum.AND, name, value);
	}

	/**
	 * 小于(<)
	 * 
	 * @param operater
	 *            逻辑运算符
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria lt(LogicalOperatorEnum operater, String name, Object value) {
		return criteria(operater, CriteriaOperatorEnum.LT, name, value);
	}

	/**
	 * And 小于(<)
	 * 
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria lt(String name, Object value) {
		return lt(LogicalOperatorEnum.AND, name, value);
	}

	/**
	 * 小于或等于(<=)
	 * 
	 * @param operater
	 *            逻辑运算符
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria le(LogicalOperatorEnum operater, String name, Object value) {
		return criteria(operater, CriteriaOperatorEnum.LE, name, value);
	}

	/**
	 * And 小于或等于(<=)
	 * 
	 * @param operater
	 *            逻辑运算符
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria le(String name, Object value) {
		return le(LogicalOperatorEnum.AND, name, value);
	}

	/**
	 * Like 包含%value%
	 * 
	 * @param operater
	 *            逻辑运算符
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria like(LogicalOperatorEnum operater, String name, Object value) {
		return criteria(operater, CriteriaOperatorEnum.LIKE, name, value);
	}

	/**
	 * and Like 包含%value%
	 * 
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria like(String name, Object value) {
		return like(LogicalOperatorEnum.AND, name, value);
	}

	/**
	 * Like 左包含 value %
	 * 
	 * @param operater
	 *            逻辑运算符
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria leftLike(LogicalOperatorEnum operater, String name, Object value) {
		return criteria(operater, CriteriaOperatorEnum.LEFT_LIKE, name, value);
	}

	/**
	 * and Like 左包含 value %
	 * 
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria leftLike(String name, Object value) {
		return leftLike(LogicalOperatorEnum.AND, name, value);
	}

	/**
	 * Like 右包含 % value
	 * 
	 * @param operater
	 *            逻辑运算符
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria rigthLike(LogicalOperatorEnum operater, String name, Object value) {
		return criteria(operater, CriteriaOperatorEnum.RIGHT_LIKE, name, value);
	}

	/**
	 * and Like 右包含 % value
	 * 
	 * @param name
	 *            属性或列名
	 * @param value
	 *            值
	 * @return
	 */
	public TCriteria rigthLike(String name, Object value) {
		return rigthLike(LogicalOperatorEnum.AND, name, value);
	}

	/**
	 * between 表达式
	 * 
	 * @param operater
	 *            逻辑运算符
	 * @param name
	 *            属性或列名称
	 * @param value
	 *            值
	 * @param secondValue
	 *            to值
	 * @return
	 */
	public TCriteria between(LogicalOperatorEnum operater, String name, Object value, Object secondValue) {
		ExceptionUtils.checkNotNull(value, "value");
		ExceptionUtils.checkNotNull(secondValue, "secondValue");
		ColumnExpression column = column(name);
		AbstractCriteriaExpression exp = AbstractExpression.binary(CriteriaOperatorEnum.BETWEEN, column,
				AbstractExpression.between(constantValue(column, value), constantValue(column, secondValue)));
		return criteria(operater, exp);
	}

	/**
	 * and between 表达式
	 * 
	 * @param name
	 *            属性或列名称
	 * @param value
	 *            值
	 * @param secondValue
	 *            to值
	 * @return
	 */
	public TCriteria between(String name, Object value, Object secondValue) {
		return between(LogicalOperatorEnum.AND, name, value, secondValue);
	}

	/**
	 * 生成
	 * 
	 * @return
	 */
	public ICriteriaSection builderSection() {
		CriteriaSectionImpl qs = new CriteriaSectionImpl(this.lockMode);
		qs.getCriterias().addAll(this.createCriteriaSections());
		return qs;
	}
}
